package com.zcl.reflection;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * @Description:
 * @author: zcl
 * @date: 2022/3/17  14:06
 *
 * 关于java.lang.Class类的理解
 * 1.类的加载过程:
 * 程序经过javac.exe命令后,或生成一个或多个字节码文件(.class结尾).
 * 接着我们使用java.exe命令对某个字节码文件进行解释运行,相当于将某个字节码文件加载到内存中.此过程就成为类的加载.加载到内存中的类,我们就成为
 * 运行时类,此运行时类,就作为Class的一个实例.
 * 2.换句话说,Class的实例就对应着一个运行时的类
 * 3.加载到内存中的运行时类, 会缓存一定的时间,在此时间内,我们可以通过不同的方式来获取此运行时的类
 *0 
 */
public class ReflectionTest {

    //反射之前
    @Test
    public void test1(){
        //1.创建Person类的对象
        Person p1 = new Person("Tom",12);

        //2.通过对象调用其内部的属性,方法
        p1.age = 10;
        System.out.println(p1.toString());

        p1.show("haha");

        //在Person类的外部,不可以通过Person可的对象,调用其内部的私有结构,比如name,showNation(),私有构造器
    }

    //反射之后
    @Test
    public void test2(){
        try {
            Class<Person> personClass = Person.class;
            //1.通过反射,创建Person类的对象
            Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
            Person tom = constructor.newInstance("Tom", 12);
            System.out.println(tom.toString());
            //2.通过反射,调用对象指定的属性,方法
            //调用属性
            Field age = personClass.getDeclaredField("age");
            age.set(tom,10);
            System.out.println(tom.toString());
            //调用方法
            Method show = personClass.getDeclaredMethod("show", String.class);
            Object showObj = show.invoke(tom, "这是show方法");
            //通过反射,,可以调用Person类的私有结构.比如私有构造器,私有属性,私有方法"

            //调用私有构造器
            Constructor<Person> constructor1 = personClass.getDeclaredConstructor(String.class);
            constructor1.setAccessible(true);
            Person person = constructor1.newInstance("张三");
            System.out.println(person.toString());

            //调用私有属性
            Field name = personClass.getDeclaredField("name");
            name.setAccessible(true);
            name.set(person,"李四");
            System.out.println(person.toString());

            //调用私有方法
            Method showNation = personClass.getDeclaredMethod("showNation", String.class);
            showNation.setAccessible(true);
            String showNationStr =(String) showNation.invoke(person, "中国");
            System.out.println(showNationStr);

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    //获取Class的实例的方式
    @Test
    public void test3(){

        //方式一: 调用运行时类的属性.class
        Class<Person> clazz1 = Person.class;
        System.out.println(clazz1.toString());
        //方式二: 通过运行时类的对象,调用getClass()方法
        Person person = new Person();
        Class<? extends Person> clazz2 = person.getClass();
        System.out.println(clazz2.toString());
        //方式三: 调用Class的静态方法: forName(String classPath)
        try {
            Class<?> clazz3 = Class.forName("com.zcl.reflection.Person");
            System.out.println(clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //方式四: 使用类的加载器: ClassLoader
        try {
            ClassLoader classLoader = ReflectionTest.class.getClassLoader();
            Class<?> clazz4 = classLoader.loadClass("com.zcl.reflection.Person");
            System.out.println(clazz4);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
